home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Diamond Collection / The Diamond Collection (Software Vault)(Digital Impact).ISO / cdr43 / sbdsp2b.zip / SBDSP.PAS < prev    next >
Pascal/Delphi Source File  |  1995-02-10  |  64KB  |  1,557 lines

  1. Unit SBDSP;
  2.                           (* Version 2.0ß *)
  3.  
  4. {$G+}           { Enable 286 instructions     }
  5. {$N+}           { Enable Numeric Coprocessor  }
  6. {$E+}           { Enable FPU emulation        }
  7. {$A+}           { Word Alignment              }
  8. {$S-}           { No Stack Checking           }
  9. {$R-}           { No Range Checking           }
  10. {$O-}           { Unit Can't be overlayed!!   }
  11. {$X+}           { Enable Extended Syntax      }
  12.  
  13. (***************************************************************************)
  14. (*              Unit for driving a SB in DMA or DIRECT MODE                *)
  15. (*                 Coming Soon: Real Time FX engine!                       *)
  16. (*               Note: Only for playing Digital samples!!!                 *)
  17. (* SBDSP is Copyright (C) 1995 by Romesh Prakashpalan, all rights reserved *)
  18. (***************************************************************************)
  19. (* Revision history:                                                       *)
  20. (*    Version 0.5α: Would not play samples across a page boundary correctly*)
  21. (*                  (I *was* getting VERY angry!)                          *)
  22. (*    Version 1.0ß: FINALLY works across page breaks, and now I shall      *)
  23. (*                  proceed to create a way to play from a virtual mem     *)
  24. (*                  file (or XMS).                                         *)
  25. (*    Version 1.01: Allright, now the unit will also work by transfering a *)
  26. (*                  file straight from disk (by using 1 buffer of 64K in   *)
  27. (*                  low memory). The reason that I didn't want to use      *)
  28. (*                  Ethan Brodsky's Unit is because he would load the      *)
  29. (*                  ENTIRE VOC file into memory at once, thus drastically  *)
  30. (*                  reducing the amnt. of memory available to the rest of  *)
  31. (*                  my program. (I load the file from disk in 32K chunks)  *)
  32. (*                  However, I do admire the fact that he was able to play *)
  33. (*                  VOC files quite nicely and efficiently!                *)
  34. (*    Version 1.1 : Now, support for 8-bit Stereo files, and the SBPRO     *)
  35. (*                  mixer have been added in.                              *)
  36. (*    Version 1.11: The Basic module for FM playing has been added in a    *)
  37. (*                  seperate unit called SBFM. Also expanded available     *)
  38. (*                  range of IRQ levels for a sound card in order to       *)
  39. (*                  maintain compatibility with some older versions. Plus, *)
  40. (*                  have added the "High Speed DMA" mode, which allows     *)
  41. (*                  playback of samples which were recorded at > 22KHz     *)
  42. (*    Version 1.2:  Added "mixing" capabilities, however, they are still a *)
  43. (*                  bit primitive though (and computationally expensive)   *)
  44. (*    Version 1.21: Cleaned up the interface section a bit, and got rid of *)
  45. (*                  IntHandlerInstalled from the Interface (Thanks goes to *)
  46. (*                  Paul Merkus! :) ), also fixed the MixSample procedure  *)
  47. (*                  so that it can mix samples that don't have the same    *)
  48. (*                  length. Note: Check out my unit for playing digital    *)
  49. (*                  samples over the GUS! I haven't tested it yet, but it  *)
  50. (*                  should work (I have an AWE 32, but would also like to  *)
  51. (*                  test out my routines on the GUS)                       *)
  52. (*    Version 1.22: Made the sample type a PChar, since pointer arithmetic *)
  53. (*                  is only defined for that type (in Turbo Pascal v7.0)   *)
  54. (*    Version 1.3 : FIXED A HUGE BUG!! High-Speed DMA xfers would not play *)
  55. (*                  back correctly, and would leave the system hanging! My *)
  56. (*                  code now compensates for this occurence! (phew)        *)
  57. (*    Version 1.3B: Bundled with the VOC2RPD program for ease of use!      *)
  58. (*    Version 1.31: Fixed a slight bug with PlaySoundRPD, which would give *)
  59. (*                  a disk read error on some very small RPD files. I have *)
  60. (*                  also provided two functions which change and check on  *)
  61. (*                  the size of the sound buffer. (This prevents the       *)
  62. (*                  programmer from changing the size of the buffer while  *)
  63. (*                  the program is playing sound!)                         *)
  64. (*    Version 1.31B:  Bundled with the VOC2RPD program AND the WAV2RPD     *)
  65. (*                  program for WAV file conversion.                       *)
  66. (*   *Version 2.0ß:   WOW! It seems like much of the code in this unit has *)
  67. (*                  evolved rather quickly (in the space of 3 months!), and*)
  68. (*                  I am already at the second MAJOR release! This ßeta    *)
  69. (*                  test unit is for people who need Double Buffering more *)
  70. (*                  than anything else. 2 Procedures have been disabled:   *)
  71. (*                  MixSamples, and PlaySoundDSK. These 2 have to be       *)
  72. (*                  converted over to Double Buffering, so since this is a *)
  73. (*                  ßeta, I do not have the time to convert ALL functions  *)
  74. (*                  over. Also note that I have added the FRAMEWORK for    *)
  75. (*                  16-bit sound, but they are NOT functional yet! I am    *)
  76. (*                  still working on it, and if you try to play 16-bit     *)
  77. (*                  sound, you are likely to get garbage played back!      *)
  78. (*                  Version 2.0 should fix all of these items.             *)
  79. (*                    This version also allows for REAL-TIME voice         *)
  80. (*                  amplification, but since it would take a lot of CPU    *)
  81. (*                  horsepower to multiply by a Floating Point amp value,  *)
  82. (*                  I had to cut down the values that can be used for      *)
  83. (*                  amplification. The reason that we are able to do REAL  *)
  84. (*                  TIME Voice amplification is because I am using Double  *)
  85. (*                  Buffers, this way, while one sound is being output,    *)
  86. (*                  another will be calculating its voice level!           *)
  87. (*                    I have fixed the re-entrancy problem (at least it    *)
  88. (*                  appears as if I have fixed it ;-) ).                   *)
  89. (*                    Also, recording was hastily thrown in (someone       *)
  90. (*                  requested it), but is fixed at 8-bit Mono currently.   *)
  91. (***************************************************************************)
  92. (* Future Releases:                                                        *)
  93. (* ----------------                                                        *)
  94. (*   Well, I think that the next great step is to make this DSP package a  *)
  95. (* REAL-TIME FX engine as well! We can do things like add echo, mix a few  *)
  96. (* samples, etc... in Real Time. This code already does Voice Amplification*)
  97. (* in Real-Time, and (my computer) doesn't seem to notice the difference,  *)
  98. (* ( < 1% CPU time on my Pentium 60), so it definately is feasible. I plan *)
  99. (* on adding FX such as Reverb, Chorus, and Echo.                          *)
  100. (***************************************************************************)
  101. (* Known Bugs:                                                             *)
  102. (* -----------                                                             *)
  103. (*   It appears as if I have FIXED the re-entrancy problems that I was     *)
  104. (* experiencing on earlier versions of this code! The only problem now, is *)
  105. (* that machines will need DOS 3.0+ in order to run it (as if anyone with  *)
  106. (* a sound card and a 386 is running anything lower than 3.31 ;-) ).       *)
  107. (***************************************************************************)
  108. (* Performance Problems (2.0ß):                                            *)
  109. (* ----------------------------                                            *)
  110. (*   This ßeta version does NOT support auto-init modes, and it is doubtful*)
  111. (* if the "real" version of this unit will either (2.0). That is probably  *)
  112. (* something that I will leave for future versions.                        *)
  113. (***************************************************************************)
  114. (* Thanks goes to the following people:                                    *)
  115. (* ------------------------------------                                    *)
  116. (* - Ethan Brodsky for his SBDSP Unit that allows one to play VOC files    *)
  117. (* over the Sound Blaster. This guy is a real hero in my book, as he shares*)
  118. (* his code with *EVERYONE*. He truly deserves the title: Sound Blaster    *)
  119. (* Code Guru! (Seems as if we agreed upon that tile, right Ethan? ;-) )    *)
  120. (* If you ever need a Sound Programmer in C/Turbo Pascal/Assembler/and     *)
  121. (* perhaps even more, don't hesitate to email him at:                      *)
  122. (*                  *  ericbrodsky@psl.wisc.edu  *                         *)
  123. (*                                                                         *)
  124. (* - Mark Feldman for his article on how to program the SB DSP chip, and   *)
  125. (* the SB PRO's Mixer chip.                                                *)
  126. (*                                                                         *)
  127. (* - Paul Merkus for his constructive comments about what I put in the     *)
  128. (* interface section of this module.                                       *)
  129. (*                                                                         *)
  130. (* - Chris Sullens for his help in finding some bugs both in SBDSP and in  *)
  131. (* VOC2RPD.                                                                *)
  132. (*                                                                         *)
  133. (* - Anthony Williams for helping me with the re-entrancy problem I was    *)
  134. (* experiencing. I definately owe him one!                                 *)
  135. (*                                                                         *)
  136. (* - And to all of the people on the comp.lang.pascal who have devoted     *)
  137. (* time to testing my code. Thanx!                                         *)
  138. (***************************************************************************)
  139. (*  For comments, help, suggestions, etc: e-mail me at:                    *)
  140. (*                   * hacscb93@huey.csun.edu *                            *)
  141. (*                                                                         *)
  142. (*                    +-------------------------+                          *)
  143. (*  OR, write me at:  |    17304 Westbury Dr.   |                          *)
  144. (*                    | Granada Hills, CA 91344 |                          *)
  145. (*                    |          USA            |                          *)
  146. (*                    +-------------------------+                          *)
  147. (*                                                                         *)
  148. (*    If you haven't the slightest clue on what I'm doing (or why I'm doing*)
  149. (*  it), don't hesitate to e-mail me, I shall endeavor to clear up the     *)
  150. (*  subject. Sound programming (especially on the Sound Blaster) is not    *)
  151. (*  THAT intuitive, so contact me if you need any help!                    *)
  152. (*                                                                         *)
  153. (*  Please let me know what I can do to make this unit better!             *)
  154. (*                                                                         *)
  155. (* Tested on my computer:                                                  *)
  156. (*  Pentium Processor @60Mhz                                               *)
  157. (*  24MB of memory                                                         *)
  158. (*  590 MB Hard Drive                                                      *)
  159. (*  Running under MSDOS 6.2 and OS/2 2.1                                   *)
  160. (*  Tested on a Sound Blaster Pro 2.0 AND Sound Blaster AWE 32 :-) (Not at *)
  161. (*  the same time of course ;-) ).                                         *)
  162. (*  SB PRO at: IO 220h, IRQ 7, DMA 1                                       *)
  163. (*  AWE 32 at: IO 220h, IRQ 5, DMA 1, HIGH DMA: 5                          *)
  164. (*                                                                         *)
  165. (*  It has also been tested on a friend's machine:                         *)
  166. (*  80386-40Mhz                                                            *)
  167. (*  Running MS-DOS 6.2                                                     *)
  168. (*  Sound Blaster 2.0 AND Sound Blaster 16 Multi CD                        *)
  169. (*  SB 2.0 at: IO 220h, IRQ 7, DMA 1                                       *)
  170. (*  SB 16  at: IO 220h, IRQ 5, DMA 1, HIGH DMA: 5                          *)
  171. (*                                                                         *)
  172. (*  And another friend:                                                    *)
  173. (*  80486-33Mhz                                                            *)
  174. (*  Running OS/2 3.0                                                       *)
  175. (*  Sound Blaster Pro 2.0, Sound Blaster 1.5                               *)
  176. (*  SB 2.0 at: IO 220h, IRQ 5, DMA 1                                       *)
  177. (*  SB 1.5 at: IO 220h, IRQ 7, DMA 1                                       *)
  178. (*                                                                         *)
  179. (* Code was compiled under the following environment:                      *)
  180. (*   DOS IDE for Turbo Pascal 7.0 (Real Mode), and also tested under the   *)
  181. (* DOS Protected mode IDE -> TPX.EXE. Sorry, no BP 7.0 :-( but the code    *)
  182. (* should also run fine on TP 6.0 (maybe minor modifications are needed)   *)
  183. (*                                                                         *)
  184. (***************************************************************************)
  185. (* Now, for some legalities:                                               *)
  186. (*   This code is FREEWARE (as of version 2.0ß), and NO ONE is to charge   *)
  187. (* anything for this code. If you distribute this document, do so WITHOUT  *)
  188. (* any changes (contact me if you think it needs changing). All derivative *)
  189. (* works are free from these limitations. You can use the code in any way  *)
  190. (* for commercial purposes, however if you mention my name in the credits  *)
  191. (* screen I would be grateful ;-). If you use this unit in a commercial    *)
  192. (* package, and are making money off of it, please send me whatever you    *)
  193. (* think this unit is worth! This way, I can do further research, and buy  *)
  194. (* upgrades to my computer.                                                *)
  195. (*   Also note that those who send a donation shall be put into a database,*)
  196. (* and will receive the latest version as soon as it comes out, as well as *)
  197. (* top priority for "technical support".                                   *)
  198. (***************************************************************************)
  199.  
  200. interface
  201.  
  202. const
  203.   (* These are the Valid constants for doing REAL-TIME Voice amplification*)
  204.   (* Since REAL TIME Voice Amplification takes a while, we must use shifts*)
  205.   (* for FAST performance!                                                *)
  206.   SilentVol    = 10;    (* 0.00x amplification *)
  207.   QuarterVol   = 5;     (* 0.25x amplification *)
  208.   HalfVol      = 3;     (* 0.50x amplification *)
  209.   NormalVol    = 0;     (* 1.00x amplification *)
  210.   One25Vol     = 7;     (* 1.25x amplification *)
  211.   One5Vol      = 9;     (* 1.50x amplification *)
  212.   DoubleVol    = 1;     (* 2.00x amplification *)
  213.   QuadrupleVol = 2;     (* 4.00x amplification *)
  214.  
  215.   (* All types of possible DMA xfers available (includes compressed modes)*)
  216.   SixteenBitDMA   = $B6;      (* Rich CD Quality Sound Output *)
  217.   EightBitDMA     = $14;      (* You'll normally need this *)
  218.   TwoBitDMA       = $16;      (* YUKK! (In most cases) *)
  219.   TwoBitRefDMA    = $17;
  220.   FourBitDMA      = $74;      (* Actually, this doesn't sound too bad! *)
  221.   FourBitRefDMA   = $75;
  222.   TwoSixBitDMA    = $76;
  223.   TwoSixBitRefDMA = $77;
  224.   EightBitDMAADC  = $24;      (* Record at a resolution of 8 bits/sample *)
  225.   HighSpeedDMAADC = $99;      (* Record at high speeds > 22Khz.          *)
  226.   HighSpeedDMA    = $91;      (* This allows playback of samples > 22kHz *)
  227.  
  228.   Mono            = 0;        (* How the channels are played back...     *)
  229.   Stereo          = 1;
  230.   Surround        = 2;
  231. type
  232.  
  233.   PhaseType = Mono..Surround;        (* Phase shift types possible       *)
  234.  
  235. (****************************************************************************)
  236. (* Voice type incorported in my programs, (I use the VOC2RPD program to     *)
  237. (* convert VOC files, and the WAV2RPD program to convert WAV files)         *)
  238. (* Channel method: 0 - Layed down Byte 1 - Channel 0, Byte 2 - Channel 1... *)
  239. (*                 1 - First Channel continuously for Size Bytes, then comes*)
  240. (*                     the Second Channel, etc...                           *)
  241. (****************************************************************************)
  242.   RPDHeader = Record
  243.                 Sig: Array [0..2] of Char; (* "RPD" *)
  244.                 Version: Word;             (* Version # *)
  245.                 DAC: Byte;                 (* 8/16/4/4.6/2/2.6, etc...*)
  246.                 Phase: PhaseType;          (* Mono=0, Stereo=1, Surround=2 *)
  247.                 Freq: Word;                (* Sample Frequency *)
  248.                 Channels: Byte;            (* # of DIGITAL Channels *)
  249.                 ChannelMethod: Byte;       (* Method for laying down channels *)
  250.                 Size: LongInt;             (* Size of Sample *)
  251.                 Signed: Boolean;           (* 16-bit Signed/Unsigned sample *)
  252.                 Reserved: Array [1..31] of Byte;
  253.               end;
  254.  
  255.   BaseSoundType = Record             (* For USER Defined sound clips     *)
  256.                    Frequency: Word;
  257.                    DACType: Byte;
  258.                    Phase: PhaseType;
  259.                    Buffer: Pointer;
  260.                    BufferSize: Word;
  261.                  end;
  262.  
  263.   (* This holds a "chunk" of up to 64K of the sample.                    *)
  264.   SoundType = Record
  265.                 Frequency: Word;     (* Frequency of the sample          *)
  266.                 DACType: Byte;       (* ADPCM Method used for compression*)
  267.                 Phase: PhaseType;    (* Indicates Stereo/Mono/Surround   *)
  268.                 Buffer1: Pointer;    (* Our Two Buffers *)
  269.                 Buffer2: Pointer;
  270.                 Buffer1Size: Word;   (* Size of the sample in our buffers*)
  271.                 Buffer2Size: Word;
  272.                 BufferPlaying: 1..2; (* Which buffer is curently playing*)
  273.               end;
  274.  
  275.   MicVolumeType = 0..7;              (* Volume resolution on the MIC-IN  *)
  276.   ProVolumeType = 0..15;             (* Volume setting Resolution on an  *)
  277.                                      (* SB-PRO                           *)
  278.   InputSourceType = 0..3;
  279.                                      (************************************)
  280.   FilterType = 0..2;                 (* Filters available on the SB PRO  *)
  281.                                      (* Filter 0 - Low Filter            *)
  282.                                      (*        1 - High Filter           *)
  283.                                      (*        2 - No Filter             *)
  284.                                      (************************************)
  285.  
  286. const
  287.  (* These are the valid parameters that can be sent to the SetInputSource*)
  288.  (* procedure:                                                           *)
  289.   Mic1Input = 0;  (* Input from Microphone #1         *)
  290.   CDInput   = 1;  (* Input from the CD                *)
  291.   Mic2Input = 2;  (* Input from Mic #2 (Can this be?) *)
  292.   LineInput = 3;  (* Input from the Line-In jack.     *)
  293.  
  294.  (* These are the valid filter values for the Sound Blaster Pro:         *)
  295.   LowFilter  = 0;
  296.   HighFilter = 1;
  297.   NoFilter   = 2;
  298.  
  299. var
  300.   CurrentSound: SoundType;           (* The current sound playing        *)
  301.   Playing: Boolean;                  (* If there is sound playing...     *)
  302.   Recording: Boolean;                (* If we are currently recording... *)
  303.  
  304. (* The following Initializes the DSP chip for data, and should always be *)
  305. (* called before you use ANY of the routines. The following are          *)
  306. (* descriptions of what the parameters require:                          *)
  307. (*                                                                       *)
  308. (* Base should be:                                                       *)
  309. (*  1 for Base Address 210h                                              *)
  310. (*  2 for Base Address 220h (Default on most cards)                      *)
  311. (*  3 for Base Address 230h                                              *)
  312. (*  etc..                                                                *)
  313. (*                                                                       *)
  314. (* IRQ should be the IRQ level of your card (usually 5 or 7)             *)
  315. (* Valid IRQs are now in the range of: 0..15 (or, in Hex: $0..$F)        *)
  316. (* DMAChannel is the DMA Channel of your sound card (usually 1)          *)
  317. (* HighDMA is the High DMA channel of your card (leave at 0 if no 16-bit *)
  318. (* DAC/ADC is on the sound card!)                                        *)
  319. (*                                                                       *)
  320. (* Returns True if DSP was detected, else returns False                  *)
  321. Function ResetDSP(Base : Byte; IRQ, DMAChannel, HighDMA: Byte) : Boolean;
  322.  
  323. (* Outputs a Byte to the Sound Card by writing directly to it...         *)
  324. Procedure WriteDAC(Level : Byte);
  325.  
  326. (* Reads a Byte from the Sound Card by reading directly to it...         *)
  327. Function ReadDAC : Byte;
  328.  
  329. (* Turns on the speaker (use before outputting sound to the card)        *)
  330. Function SpeakerOn: Byte;
  331.  
  332. (* Turns off the speaker (does not affect DMA transfers, e.g: DMA xfers  *)
  333. (* will still occur, but you will not HEAR them)                         *)
  334. Function SpeakerOff: Byte;
  335.  
  336. (* Pauses a DMA xfer                                                     *)
  337. Procedure DMAStop;
  338.  
  339. (* Continues a paused DMA xfer                                           *)
  340. Procedure DMAContinue;
  341.  
  342. (* Use the following to play a sound up to 64K long                      *)
  343. Procedure PlaySound (Sound: BaseSoundType);
  344.  
  345. (* This will play a sound file from DISK (using a 64K buffer), with the  *)
  346. (* specified frequency and compression algorithm in SoundType.           *)
  347. Procedure PlaySoundDSK (Filename: String; Frequency: Word; SoundType: Byte);
  348.  
  349. (* This will play a sound file (RPD) from disk, so all you have to do is *)
  350. (* specify the Filename (no other settings are required)                 *)
  351. Procedure PlaySoundRPD (Filename: String);
  352.  
  353. (* This will Load a RPD file's contents (up to 64K) into a memory buffer *)
  354. (* specified. If MemAlloc is True then the procedure will allocate       *)
  355. (* memory to the buffer, else the user has already allocated a suitable  *)
  356. (* amount, and the program will not allocate memory for it (could result *)
  357. (* in a Crash if Memory HASN'T been allocated...                         *)
  358. Procedure LoadSoundRPD (Filename: String; var Sound: BaseSoundType;
  359.           MemAlloc: Boolean);
  360.  
  361. (* This will record sound from the MICROPHONE to a RPD file on disk, use *)
  362. (* the StopRecording command to stop the recording...                    *)
  363. Procedure RecordSoundRPD (Filename: String; Freq: Word);
  364.  
  365. (* Stops a recording initiated by RecordSoundRPD.                        *)
  366. Function StopRecording: Boolean;
  367.  
  368. (* Installs the SB Interrupt Hook                                        *)
  369. Procedure InstallHandler;
  370.  
  371. (* Uninstalls the SB Interrupt Hook                                      *)
  372. Procedure UninstallHandler;
  373.  
  374. (* Read a Byte from the DSP chip                                         *)
  375. Function  ReadDSP : Byte;
  376.  
  377. (* Write a Byte to the DSP chip                                          *)
  378. Procedure WriteDSP (Value : Byte);
  379.  
  380. (* Gets the DSP chip version number                                      *)
  381. Function GetDSPVersion: String;
  382.  
  383. (* Mixes the two input parameters: Buffer1 and Buffer2, and returns it in*)
  384. (* the Result parameter. Note: The Result Parameter needs to be allocated*)
  385. (* prior to the call of this function!                                   *)
  386. Procedure MixSamples (Buffer1, Buffer2: BaseSoundType;var Result:BaseSoundType);
  387.  
  388. (* Changes the size of the sound buffer (used for Disk xfers). A higher  *)
  389. (* value provides smoother playback (albeit at the cost of memory), and  *)
  390. (* a smaller one takes up less memory (but sounds choppier).             *)
  391. (* Returns:                                                              *)
  392. (*    True if Buffer size was changed (no sound was playing)             *)
  393. (*    False if Buffer size wasn't changed (sound was playing)            *)
  394. Function ChangeBufferSize (Size: Word): Boolean;
  395.  
  396. (* Returns the size of the allocated sound buffer.                       *)
  397. Function CheckBufferSize: Word;
  398.  
  399. (* Changes the *Global* volume level (use the constants in the interface *)
  400. (* section. One note on volume level: If you increase the volume level   *)
  401. (* TOO Much, then you are going to get values above the 255 volume range!*)
  402. (* This, needless to say, is NOT too good! You will end up getting VERY  *)
  403. (* distorted sounds. So, unless you have a VERY SOFT sample, don't change*)
  404. (* this Amount. However, if you have TOO LOUD of a sample, you could     *)
  405. (* make it SOFTER. Anyways, this is the first REAL-TIME code in this     *)
  406. (* unit, and I am pleased with its performance (I don't know about any   *)
  407. (* one else's machine yet, but on my Pentium 60, I can't tell the        *)
  408. (* difference in speed ;-) ). But really, I did this function up really  *)
  409. (* quick to see the feasibility of REAL-TIME Sound Programming!          *)
  410. Procedure ChangeVolumeLevel (Amnt: Byte);
  411.  
  412. (* Returns the *Global* volume level                                     *)
  413. Function GetVolumeLevel: Byte;
  414.  
  415. (* Converts a signed 16-bit sample to a 8-bit unsigned sample...         *)
  416. Procedure Convert16to8 (var SixteenBitSound, EightBitSound: BaseSoundType);
  417.  
  418. (*+---------------------------------------------------------------------+*)
  419. (*|The Following routines should only be used with an SB PRO or higher: |*)
  420. (*+---------------------------------------------------------------------+*)
  421.  
  422. (* Sets the Mixer to Mono mode                                           *)
  423. Procedure PlayMono;
  424.  
  425. (* Sets the Mixer to Stereo mode                                         *)
  426. (* VOC files are arranged differently in Stereo modes (odd bytes -> left *)
  427. (* channel, even bytes -> right channel)                                 *)
  428. Procedure PlayStereo;
  429.  
  430. (* Resets the CT 1345 Mixer chip (call before using any other functions) *)
  431. Procedure ResetMixer;
  432.  
  433. (* Sets the Voice Level on the Sound Card *)
  434. Procedure SetVocVolume (Left, Right: ProVolumeType);
  435.  
  436. (* Sets the FM Level on the Sound Card *)
  437. Procedure SetFMVolume (Left, Right: ProVolumeType);
  438.  
  439. (* Sets the CD Level on the Sound Card *)
  440. Procedure SetCDVolume (Left, Right: ProVolumeType);
  441.  
  442. (* Sets the Master Volume on the Sound Card *)
  443. Procedure SetMasterVolume (Left, Right: ProVolumeType);
  444.  
  445. (* Sets the Mic in Volume Level on the Sound Card, before recording, make *)
  446. (* sure that you set this to a non-zero value!                            *)
  447. Procedure SetMicVolume (Volume: MicVolumeType);
  448.  
  449. (* Sets the Line in Volume Level, see notes on the Mic In Volume level for*)
  450. (* more details...                                                        *)
  451. Procedure SetLineInVolume (Left, Right: ProVolumeType);
  452.  
  453. (* Selects the Input Source (Line In, Mic, etc...) Use ONLY the valid     *)
  454. (* constants!! Also, remember to specify a filter!                        *)
  455. Procedure SetInputSource (Filter: FilterType; TheInputs: InputSourceType);
  456.  
  457. implementation
  458. Uses Crt, DOS, Memory;
  459.  
  460. const
  461.   (************************************************************************)
  462.   (* Notes on the Buffer Size:                                            *)
  463.   (* -------------------------                                            *)
  464.   (* I generally find that values in the range of 16K to 64K are the best *)
  465.   (* to use, (16K saves memory, and it still allows decent performance,   *)
  466.   (* especially if the sample rate <= 11Khz, and 64K allows the BEST      *)
  467.   (* performance possible, but takes away some precious memory space). I  *)
  468.   (* usually prefer 32K ($7FFF) => Nice balance. But, when playing 16-bit *)
  469.   (* sound at 44.1KHz stereo, note that we will be processing: 176,400    *)
  470.   (* bytes/second! This means that with a buffer size of 64K, we will hit *)
  471.   (* the interrupt handler 2.7x/sec! This could degrade system performance*)
  472.   (* if you are doing heavy duty graphics processing at the same time, so *)
  473.   (* I would advise that you use a lower sampling rate and/or depth.      *)
  474.   (* Unless of course, you are making a music player.                     *)
  475.   (************************************************************************)
  476.  
  477.   MaxXferSize: Word = $7FFF;  (* Double Buffer Size *)
  478.  
  479.  (************************************************************************)
  480.  (* Note: In previous versions of the program, the user was able to      *)
  481.  (* ----  change the size of the buffer as the program goes along,       *)
  482.  (*       without having to check if the program was playing sound at    *)
  483.  (*       that current moment. Now, this variable is isolated, and to    *)
  484.  (*       change/check on the size of the currently allocated buffer, one*)
  485.  (*       has to make the following calls: ChangeBufferSize, and         *)
  486.  (*       CheckBufferSize. This way, the user doesn't "kill" the system  *)
  487.  (*       in some weird way! Also, experiment with this value, especially*)
  488.  (*       when you are doing Real-Time FX.                               *)
  489.  (************************************************************************)
  490.  
  491. type
  492.   BytePtr = ^Byte;
  493. var
  494.   SBBase : Word;
  495.   SBDMA  : Byte;
  496.   SBIRQ  : Byte;
  497.   SBDMA16: Byte;
  498.  
  499.   DSP_RESET        : Word;
  500.   DSP_READ_DATA    : Word;
  501.   DSP_WRITE_DATA   : Word;
  502.   DSP_WRITE_STATUS : Word;
  503.   DSP_DATA_AVAIL   : Word;
  504.   DSP_INT_ACK16    : Word;
  505.   DSPPRO_READWRITE : Word;
  506.   DSPPRO_INDEX     : Word;
  507.   PlayingSixteenBit: Boolean;
  508.  
  509.   InDOSFlag: BytePtr;
  510.  
  511.   DMAMaskPort     : Word;
  512.   DMAClrPtrPort   : Word;
  513.   DMAModePort     : Word;
  514.   DMABaseAddrPort : Word;
  515.   DMACountPort    : Word;
  516.   DMAPagePort     : Word;
  517.  
  518.   DMAStartMask    : Byte;
  519.   DMAStopMask     : Byte;
  520.   DMAMode         : Byte;
  521.  
  522.   Regs            : Registers;
  523.   TurboPSP        : Word;
  524.   TempPSP         : Word;
  525.  
  526.   LastSavedBuffer : Byte;
  527.  
  528.   IntHandler: Byte;
  529.   OldIntHandler: Procedure;
  530.   PicPort: Byte;
  531.   IRQStopMask, IRQStartMask: Byte;
  532.   CurPos: LongInt;
  533.   CurPageEnd: LongInt;
  534.   Length: Word;
  535.   LeftToPlay: LongInt;
  536.   VoiceEnd: LongInt;
  537.   DSKFile: File;
  538.   FileXferLeft: LongInt;
  539.   PlayFromDisk: Boolean;
  540.   OldExitProc: Pointer;
  541.   FinishUp: Boolean;
  542.   TotalRecorded: Longint;
  543.  
  544. const
  545.   IRQHandlerInstalled: Boolean = False;
  546.   SixteenBitCapable: Boolean = False;
  547.   AutoInitCapable: Boolean = False;
  548.   CurrentVolLevel: Byte = NormalVol;
  549.   PlaySurround: Boolean = False;
  550.  
  551. Procedure Convert8to16 (var EightBitSound, SixteenBitSound: BaseSoundType);
  552. Begin
  553. End;
  554.  
  555. Procedure Convert16to8 (var SixteenBitSound, EightBitSound: BaseSoundType);
  556. (***************************************************************************)
  557. (* Converts a 16-bit Signed sample in SixteenBitSound to a 8-bit unsigned  *)
  558. (* sample in EightBitSound. Frequency is preserved, but DACType and size   *)
  559. (* are changed (EightBitSize := SixteenBitSize div 2)                      *)
  560. (* Each sample word -> 1 Byte in the output buffer                         *)
  561. (***************************************************************************)
  562. var
  563.   Dest, Source: Pointer;
  564.   Amnt: Word;
  565. Begin
  566.   EightBitSound.DACType := EightBitDMA;
  567.   EightBitSound.Frequency := SixteenBitSound.Frequency;
  568.   EightBitSound.BufferSize := SixteenBitSound.BufferSize div 2;
  569.   Dest := EightBitSound.Buffer;
  570.   Source := SixteenBitSound.Buffer;
  571.   Amnt := SixteenBitSound.BufferSize;
  572.   asm
  573.     CLD                 {; Clear the Direction flag }
  574.     MOV CX, AMNT        {; Number of BYTES to xfer  }
  575.     LES DI, [DEST]      {; ES:DI = Destination Buffer }
  576.     LDS SI, [SOURCE]    {; DS:SI = Source Buffer      }
  577.  @GETNEWSAMPLE:
  578.     LODSW               {; Load a WORD (Unsigned) from the 16-bit sound }
  579.     SHR AX, 9           {; Convert Word -> Byte                         }
  580.     ADD AL, 128         {; Unsigned -> Signed                           }
  581.     STOSB               {; Store the resulting Byte                     }
  582.     LOOP @GETNEWSAMPLE  {; Finish off the REST of the Sample Data       }
  583.     MOV AX, SEG @DATA   {; Restore Turbo Pascal's Data Segment          }
  584.     MOV DS, AX
  585.   end;
  586. End;
  587.  
  588. Function GetAbsoluteAddress (P: Pointer): LongInt;
  589. (* Faster when implemented in assembly (and easier!)    *)
  590. (* Uses the only 32-bit code in this unit               *)
  591. Begin
  592.   asm
  593.     DB 66h; XOR AX, AX                 {; Make sure that no "junk" is in the}
  594.     DB 66h; XOR BX, BX                 {; extended Registers}
  595.     MOV AX, WORD PTR [P+2]             {; AX := SEG (P^) }
  596.     MOV BX, WORD PTR [P]               {; BX := OFS (P^) }
  597.     DB 66h; SHL AX, 4                  {; EAX := SEG (P^) * 16}
  598.     DB 66h; ADD AX, BX                 {; EAX := EAX + OFS (P^)}
  599.     DB 66h; MOV WORD PTR @RESULT, AX   {; GetAbsoluteAddress := EAX }
  600.   end;
  601. End;
  602.  
  603. Function NormalizePtr (P: Pointer): Pointer;
  604. (* Returns Normalized Pointer of P *)
  605. var
  606.   LinearAddr: LongInt;
  607. Begin
  608.   LinearAddr := GetAbsoluteAddress (P);
  609.   NormalizePtr := Ptr (LinearAddr div 16, LinearAddr mod 16);
  610. End;
  611.  
  612. Function MemAllocPage (Size: Word): Pointer;
  613. (* Returns a Pointer that is allocated on a PAGE boundary, NOT a SEGMENT *)
  614. (* boundary, as DMA xfers are based on pages...                          *)
  615. var
  616.   P: Pointer;
  617. Begin
  618.   GetMem (P, Size);
  619.   If (GetAbsoluteAddress (P) mod 65536) + Size < 65536 then
  620.     MemAllocPage := P
  621.   Else MemAllocPage := NormalizePtr (Ptr (Seg(P^), Ofs (P^) + Size));
  622. End;
  623.  
  624. Function OutputSurround: Boolean;
  625. Begin
  626.   OutputSurround := False;
  627.   If not Playing then
  628.   Begin
  629.     OutputSurround := True;
  630.     PlaySurround := True;
  631.   End;
  632. End;
  633.  
  634. Function ShutOffSurround: Boolean;
  635. Begin
  636.   ShutOffSurround := False;
  637.   If not Playing then
  638.   Begin
  639.     ShutOffSurround := True;
  640.     PlaySurround := False;
  641.   End;
  642. End;
  643.  
  644. Procedure ChangeSampleVol (Buffer: PChar; Size: Word);
  645. (*          Quick function to change the Sample Volume!          *)
  646. var
  647.   I: Word;
  648.   ShrConstant: Byte;
  649. Begin
  650.   If CurrentVolLevel = NormalVol then Exit;
  651.   If CurrentVolLevel = SilentVol then
  652.     FillChar (Buffer^, Size, 0)
  653.   Else if CurrentVolLevel in [DoubleVol, QuadrupleVol] then
  654.     For I := 0 to Size do
  655.       Buffer [I] := Char (Byte (Buffer [I]) shl CurrentVolLevel)
  656.   Else if CurrentVolLevel in [HalfVol, QuarterVol] then
  657.   Begin
  658.    If CurrentVolLevel = HalfVol then
  659.      ShrConstant := 1
  660.    Else ShrConstant := 2;
  661.     For I := 0 to Size do
  662.       Buffer [I] := Char (Byte (Buffer [I]) shr ShrConstant);
  663.   End
  664.   Else if CurrentVolLevel in [One25Vol, One5Vol] then
  665.   Begin
  666.     If CurrentVolLevel = One25Vol then
  667.       ShrConstant := 2
  668.     Else ShrConstant := 1;
  669.     For I := 0 to Size do
  670.       Buffer [I] := Char (Byte (Buffer[I]) + Byte (Buffer[I]) shr ShrConstant);
  671.   End;
  672. End;
  673.  
  674. Procedure ChangeVolumeLevel (Amnt: Byte);
  675. Begin
  676.   CurrentVolLevel := Amnt;
  677. End;
  678.  
  679. Function GetVolumeLevel: Byte;
  680. Begin
  681.   GetVolumeLevel := CurrentVolLevel;
  682. End;
  683.  
  684. Procedure MixSamples (Buffer1, Buffer2: BaseSoundType;var Result:BaseSoundType);
  685. var
  686.   Temp1, Temp2, TempC: PChar;
  687.   CurrentByte: Word;
  688. Begin
  689.   (* Figure out the size of the resulting sample (Max [Buffer1, Buffer2]) *)
  690.   If Buffer1.BufferSize > Buffer2.BufferSize then
  691.     Result.BufferSize := Buffer1.BufferSize
  692.   else Result.BufferSize := Buffer2.BufferSize;
  693.   (* Assuming that both frequencies are the same *)
  694.   Result.Frequency := Buffer1.Frequency;
  695.  
  696.   (* Assuming that both DAC types are the same *)
  697.   CurrentSound.DACType := Buffer1.DACType;
  698.   CurrentByte := 0;
  699.   Temp1 := Buffer1.Buffer;
  700.   Temp2 := Buffer2.Buffer;
  701.   TempC := Result.Buffer;
  702.   Repeat
  703.     (* Simple scaling function to "mix" the samples *)
  704.     TempC^ := Char ((((Byte (Temp2^) * 2) - Byte (Temp1^))*2) div 2);
  705.     Inc (TempC);
  706.     If (CurrentByte < Buffer1.BufferSize) then
  707.       Inc (Temp1)
  708.     Else
  709.       Temp1^ := #0;
  710.     If (CurrentByte < Buffer2.BufferSize) then
  711.       Inc (Temp2)
  712.     Else
  713.       Temp2^ := #0;
  714.     Inc (CurrentByte);
  715.   Until (CurrentByte >= Result.BufferSize);
  716. End;
  717.  
  718. Function GetDSPVersion: String;
  719. var
  720.   MajorByte, MinorByte: Byte;
  721.   MajorStr, MinorStr: String;
  722. Begin
  723.   WriteDSP ($E1);
  724.   MajorByte := ReadDSP;
  725.   If MajorByte >= 4 then
  726.     SixteenBitCapable := True
  727.   else SixteenBitCapable := False;
  728.   If MajorByte >= 2 then
  729.     AutoInitCapable := True
  730.   Else AutoInitCapable := False;
  731.   Str (MajorByte, MajorStr);
  732.   MinorByte := ReadDSP;
  733.   Str(MinorByte, MinorStr);
  734.   If MinorByte < 10 then MinorStr := '0' + MinorStr;
  735.   GetDSPVersion := MajorStr + '.' + MinorStr;
  736. End;
  737.  
  738. Procedure WriteDSP(Value: Byte);
  739. Begin
  740.   While Port[DSP_WRITE_STATUS] and $80 <> 0 do;
  741.     Port[DSP_WRITE_DATA] := Value;
  742. End;
  743.  
  744. Function ReadDSP: Byte;
  745. Begin
  746.   While Port[DSP_DATA_AVAIL] and $80 = 0 do;
  747.     ReadDSP := Port[DSP_READ_DATA];
  748. End;
  749.  
  750. Procedure WriteDAC(Level: Byte);
  751. Begin
  752.   WriteDSP($10);
  753.   WriteDSP(Level);
  754. End;
  755.  
  756. Function ReadDAC: Byte;
  757. Begin
  758.   WriteDSP($20);
  759.   ReadDAC := ReadDSP;
  760. End;
  761.  
  762. Function SpeakerOn: Byte;
  763. Begin
  764.   WriteDSP($D1);
  765. End;
  766.  
  767. Function SpeakerOff: Byte;
  768. Begin
  769.   WriteDSP($D3);
  770. End;
  771.  
  772. Procedure DMAContinue;
  773. Begin
  774.   WriteDSP($D4);
  775. End;
  776.  
  777. Procedure DMAStop;
  778. Begin
  779.   WriteDSP($D0);
  780. End;
  781.  
  782. Procedure InitSB16for16Out;
  783. Begin
  784.   DMAMaskPort     := $D4;
  785.   DMAClrPtrPort   := $D8;
  786.   DMAModePort     := $D6;
  787.   DMABaseAddrPort := $C0 + 4 * (SBDMA16 - 4);
  788.   DMACountPort    := $C2 + 4 * (SBDMA16 - 4);
  789.   Case SBDMA16 of
  790.     5:  DMAPagePort := $8B;
  791.     6:  DMAPagePort := $89;
  792.     7:  DMAPagePort := $8A;
  793.   end;
  794.   DMAStopMask  := (SBDMA16 - 4) + $04;
  795.   DMAStartMask := (SBDMA16 - 4) + $00;
  796.   DMAMode      := (SBDMA16 - 4) + $48;  (* Non-Auto Init Mode... *)
  797. End;
  798.  
  799. Procedure InitSB16for8Out;
  800. Begin
  801.   DMAMaskPort     := $0A;
  802.   DMAClrPtrPort   := $0C;
  803.   DMAModePort     := $0B;
  804.   DMABaseAddrPort := 2 * SBDMA;
  805.   DMACountPort    := 2 * SBDMA + 1;
  806.   Case SBDMA of
  807.     0:  DMAPagePort := $87;
  808.     1:  DMAPagePort := $83;
  809.     2:  DMAPagePort := $81;
  810.     3:  DMAPagePort := $82;
  811.   end;
  812.   DMAStopMask  := SBDMA + $04;   (* Non-Auto Init Mode... *)
  813.   DMAStartMask := SBDMA + $00;
  814.   DMAMode      := SBDMA + $48;
  815. End;
  816.  
  817. Function ResetDSP(Base : Byte; IRQ, DMAChannel, HighDMA: Byte) : Boolean;
  818.    (* Returns TRUE if a DSP chip was found, else returns FALSE *)
  819. const
  820.    (* The IRQ Interrupt numbers available *)
  821.    IRQIntNums : Array[0..15] of Byte =
  822.                 ($08, $09, $0A, $0B, $0C, $0D, $0E, $0F,
  823.                  $70, $71, $72, $73, $74, $75, $76, $77);
  824.  
  825. Begin
  826. (* Initialize Global variables *)
  827.   SBBase := Base;
  828.   SBIRQ := IRQ;
  829.   SBDMA := DMAChannel;
  830.   SBDMA16 := HighDMA;
  831.  
  832.   If IRQ <= 7 then PICPort := $21 else PICPort := $A1;
  833.  
  834.   IRQStopMask  := 1 SHL (IRQ MOD 8);
  835.   IRQStartMask := Not (IRQStopMask);
  836.  
  837.   Base := Base * $10;
  838.  
  839.   (* Calculate the port addresses *)
  840.   DSP_RESET        := Base + $206;
  841.   DSP_READ_DATA    := Base + $20A;
  842.   DSP_WRITE_DATA   := Base + $20C;
  843.   DSP_WRITE_STATUS := Base + $20C;
  844.   DSP_DATA_AVAIL   := Base + $20E;
  845.   DSP_INT_ACK16    := Base + $20F;
  846.   DSPPRO_READWRITE := Base + $205;
  847.   DSPPRO_INDEX     := Base + $204;
  848.  
  849.   GetDSPVersion;  (* Just to check if we have a 16-bit card! *)
  850.  
  851.  (************************************************************************)
  852.  (* Even if you have a Sound Blaster 16 card, this procedure initializes *)
  853.  (* it for 8-bit playback! So, use the InitSB16for16Out procedure before *)
  854.  (* attempting to play 16-bit sounds (unless you are using PlaySoundRPD, *)
  855.  (* and in that case, my code automatically compensates). I chose to     *)
  856.  (* write it this way for compatibility.                                 *)
  857.  (************************************************************************)
  858.  
  859.   DMAMaskPort     := $0A;
  860.   DMAClrPtrPort   := $0C;
  861.   DMAModePort     := $0B;
  862.   DMABaseAddrPort := 2 * DMAChannel;
  863.   DMACountPort    := 2 * DMAChannel + 1;
  864.   Case DMAChannel of
  865.     0:  DMAPagePort := $87;
  866.     1:  DMAPagePort := $83;
  867.     2:  DMAPagePort := $81;
  868.     3:  DMAPagePort := $82;
  869.   end;
  870.   DMAStopMask  := DMAChannel + $04;
  871.   DMAStartMask := DMAChannel + $00;
  872.   DMAMode      := DMAChannel + $48;
  873.   (* Reset the DSP, and give some nice long delays just to be safe *)
  874.   Port[DSP_RESET] := 1;
  875.   Delay(10);
  876.   Port[DSP_RESET] := 0;
  877.   Delay(10);
  878.   if (Port[DSP_DATA_AVAIL] and $80 = $80) and (Port[DSP_READ_DATA] = $AA) then
  879.     ResetDSP := True
  880.   else
  881.     ResetDSP := False;
  882.   IntHandler := IRQIntNums[IRQ];
  883.   InstallHandler;
  884. end;
  885.  
  886. Procedure Playback; near;
  887. (***************************************************************************)
  888. (* This procedure should NOT be called by anyone outside of this unit, as  *)
  889. (* its prime purpose is to start a DMA xfer, or get called by the Interrupt*)
  890. (* handler when a xfer has to be continued!                                *)
  891. (***************************************************************************)
  892. var
  893.   Time_Constant : Word;
  894.   Page, Offset : Word;
  895. begin
  896.   (* Set up the DMA chip, by setting the Page and Offsets *)
  897.   If PlayingSixteenBit then
  898.   Begin
  899.     Page := (CurPos div 2) div 65536;      (* Divide by 2 for 16-bit xfers *)
  900.     Offset := (CurPos div 2) mod 65536;
  901.   End
  902.   Else
  903.   Begin
  904.     Page := CurPos div 65536;
  905.     Offset := CurPos mod 65536;
  906.   End;
  907.   If VoiceEnd < CurPageEnd then
  908.     Length := LeftToPlay-1
  909.   Else
  910.     Length := CurPageEnd - CurPos;
  911.  
  912.   Inc(CurPos, LongInt(Length)+1);
  913.   Dec(LeftToPlay, LongInt(Length)+1);
  914.   Inc(CurPageEnd, 65536);
  915.  
  916.   (* Set the playback frequency (Note: Not used for 16-bit xfers) *)
  917.   If not PlayingSixteenBit then
  918.     Time_Constant := 256 - (1000000 div CurrentSound.Frequency);
  919.  
  920.   (* Now, we must program the DMA chip for another xfer *)
  921.   Port[DMAMaskPort]     := DMAStopMask;  (* Stop any DMA activity so far *)
  922.   Port[DMAClrPtrPort]   := $00;
  923.   Port[DMAModePort]     := DMAMode;
  924.   Port[DMABaseAddrPort] := Lo(Offset);
  925.   Port[DMABaseAddrPort] := Hi(Offset);
  926.   If PlayingSixteenBit then
  927.   Begin
  928.     Port[DMACountPort]    := Lo(Length div 2); (* # of WORDS to xfer *)
  929.     Port[DMACountPort]    := Hi(Length div 2);
  930.   End
  931.   Else
  932.   Begin
  933.     Port[DMACountPort]    := Lo(Length);       (* # of BYTES to xfer *)
  934.     Port[DMACountPort]    := Hi(Length);
  935.   End;
  936.   Port[DMAPagePort]     := Page;
  937.   Port[DMAMaskPort]     := DMAStartMask; (* Start the DMA transfer *)
  938.  
  939.   If PlayingSixteenBit then
  940.   Begin
  941.     WriteDSP ($41);
  942.     WriteDSP (Hi (CurrentSound.Frequency)*2);
  943.     WriteDSP (Lo (CurrentSound.Frequency)*2);
  944.     WriteDSP ($B2);      { 10111010 }
  945.     If CurrentSound.Phase = Stereo then
  946.       WriteDSP ($20)     { 00xx0000 }
  947.     Else WriteDSP ($10);
  948.     WriteDSP (Lo (Length) div 2);
  949.     WriteDSP (Hi (Length) div 2);
  950.   End
  951.   Else
  952.   Begin
  953.     WriteDSP($40);
  954.     WriteDSP(Time_constant);
  955.     If CurrentSound.DACType = HighSpeedDMA then
  956.     Begin
  957.       WriteDSP ($48);
  958.       WriteDSP (Lo (Length));
  959.       WriteDSP (Hi (Length));
  960.       WriteDSP ($91);
  961.     End
  962.     Else
  963.     Begin
  964.       WriteDSP(CurrentSound.DACType);
  965.       WriteDSP(Lo(Length));
  966.       WriteDSP(Hi(Length));
  967.     End;
  968.   End;
  969. End;
  970.  
  971. Procedure PlaySoundDSK (Filename: String; Frequency: Word; SoundType: Byte);
  972. Begin
  973.   Assign (DSKFile, Filename);
  974.   Reset (DSKFile, 1);
  975.   CurrentSound.Frequency := Frequency;
  976.   CurrentSound.DACType := SoundType;
  977.   FileXferLeft := FileSize (DSKFile);
  978.   PlayFromDisk := True;
  979.   If FileSize (DSKFile) > MaxXferSize then
  980.   Begin
  981.     CurrentSound.Buffer1Size := MaxXferSize;
  982.     BlockRead (DSKFile, CurrentSound.Buffer1^, MaxXferSize);
  983.     Dec (FileXferLeft, MaxXferSize);
  984.   End
  985.   Else
  986.   Begin
  987.     CurrentSound.Buffer1Size := FileSize (DSKFile);
  988.     BlockRead (DSKFile, CurrentSound.Buffer1^, Filesize (DSKFile));
  989.     Dec (FileXferLeft, FileSize (DSKFile));
  990.   End;
  991.   LeftToPlay := CurrentSound.Buffer1Size - 6;
  992.   CurPos := GetAbsoluteAddress (CurrentSound.Buffer1) + 6;
  993.   CurPageEnd := ((CurPos shr 16) shl 16) + 65536 - 1;
  994.   Length := CurPageEnd - CurPos;
  995.   VoiceEnd := CurPos + LeftToPlay;
  996.   Playing := True;
  997.   PlayFromDisk := True;
  998.   SpeakerOn;
  999.   PlayBack;
  1000. End;
  1001.  
  1002. Procedure RecordSoundRPD (Filename: String; Freq: Word);
  1003. (*****************************************************************************)
  1004. (* Record to a File (Filename), with 8bits/sample at the frequency specified *)
  1005. (* (Note: Sound will record UNTIL StopRecording is issued!)                  *)
  1006. (*****************************************************************************)
  1007. var
  1008.   TempHead: RPDHeader;
  1009. Begin
  1010.   TotalRecorded := 0;
  1011.   CurrentSound.DACType := EightBitDMAADC;            (* Input = ADC *)
  1012.   CurrentSound.Frequency := Freq;
  1013.   Assign (DSKFile, Filename);
  1014.   Rewrite (DSKFile, 1);
  1015.   BlockWrite (DSKFile, TempHead, SizeOf (TempHead)); (* Write "garbage" to *)
  1016.                                                      (* the header, we'll  *)
  1017.                                                      (* fill it in later!  *)
  1018.   CurrentSound.Phase := Mono;     (* We'll only record in MONO!            *)
  1019.   CurrentSound.BufferPlaying := 1;
  1020.   FillChar (CurrentSound.Buffer1^, CheckBufferSize div 2, 0);
  1021.   FillChar (CurrentSound.Buffer2^, CheckBufferSize div 2, 0);
  1022.   DMAMode := SBDMA + $44;         (* THE NEW DMA MODE! *)
  1023.   CurrentSound.Buffer1Size := MaxXferSize div 2;
  1024.   CurrentSound.Buffer2Size := MaxXferSize div 2;
  1025.   LeftToPlay := CurrentSound.Buffer1Size - 6;
  1026.   CurPos := GetAbsoluteAddress (CurrentSound.Buffer1) + 6;
  1027.   CurPageEnd := ((CurPos shr 16) shl 16) + 65536 - 1;
  1028.   Length := CurPageEnd - CurPos;
  1029.   VoiceEnd := CurPos + LeftToPlay;
  1030.   Recording := True;
  1031.   SpeakerOn;
  1032.   LastSavedBuffer := 2;
  1033.   PlayBack;
  1034. End;
  1035.  
  1036. Function StopRecording: Boolean;
  1037. (***************************************************************************)
  1038. (*   MUST be called to stop the recording! Returns TRUE if there was a     *)
  1039. (* recording in progress, FALSE otherwise...                               *)
  1040. (***************************************************************************)
  1041.  
  1042. var
  1043.   TempHead: RPDHeader;
  1044. Begin
  1045.   StopRecording := False;
  1046.   If Recording then
  1047.   Begin
  1048.     Port [DMAMaskPort] := DMAStopMask;  (* STOP DMA Xfer! *)
  1049.     DMAStop;                            (* Stop the Sound Card too *)
  1050.     StopRecording := True;
  1051.     TempHead.DAC := EightBitDMA;
  1052.     TempHead.Phase := Mono;
  1053.     TempHead.Freq := CurrentSound.Frequency;
  1054.     TempHead.Size := TotalRecorded;
  1055.     TempHead.Channels := 1;
  1056.     If LastSavedBuffer = 1 then
  1057.       BlockWrite (DSKFile, CurrentSound.Buffer2^, MaxXferSize div 2)
  1058.     Else
  1059.       BlockWrite (DSKFile, CurrentSound.Buffer1^, MaxXferSize div 2);
  1060.     Seek (DSKFile, 0);
  1061.     BlockWrite (DSKFile, TempHead, SizeOf (TempHead));
  1062.     Close (DSKFile);
  1063.     Recording := False;
  1064.   End;
  1065. End;
  1066.  
  1067. Procedure LoadSoundRPD (Filename: String; var Sound: BaseSoundType;
  1068.           MemAlloc: Boolean);
  1069. (***************************************************************************)
  1070. (*  Loads a RPD file into a buffer (up to 64K). MemAlloc should be True if *)
  1071. (* the buffer hasn't been allocated yet, False if a buffer in memory has   *)
  1072. (* already been allocated...                                               *)
  1073. (***************************************************************************)
  1074.  
  1075. var
  1076.   TempHead: RPDHeader;
  1077.   TempFile: File;
  1078. Begin
  1079.   Assign (TempFile, Filename);
  1080.   Reset (TempFile, 1);
  1081.   BlockRead (TempFile, TempHead, SizeOf (TempHead));
  1082.   Sound.Frequency := TempHead.Freq;
  1083.   Sound.DACType := TempHead.DAC;
  1084.   If not SixteenBitCapable then
  1085.     If Sound.Frequency >= 23000 then
  1086.       Sound.DACType := HighSpeedDMA;
  1087.   Sound.Phase := TempHead.Phase;
  1088.   If FileSize (TempFile) - SizeOf (TempHead) > $FFFF then
  1089.     Sound.BufferSize := $FFFF
  1090.   else Sound.BufferSize := FileSize (TempFile) - SizeOf (TempHead);
  1091.   If MemAlloc then
  1092.     GetMem (Sound.Buffer, Sound.BufferSize);
  1093.   BlockRead (TempFile, Sound.Buffer^, Sound.BufferSize);
  1094.   ChangeSampleVol (Sound.Buffer, Sound.BufferSize);
  1095.   Close (TempFile);
  1096. End;
  1097.  
  1098. Procedure PlaySoundRPD (Filename: String);
  1099. var
  1100.   TempHead: RPDHeader;
  1101. Begin
  1102.   Assign (DSKFile, Filename);
  1103.   Reset (DSKFile, 1);
  1104.   BlockRead (DSKFile, TempHead, Sizeof (TempHead));
  1105.   CurrentSound.Frequency := TempHead.Freq;
  1106.   CurrentSound.DACType := TempHead.DAC;
  1107.   If CurrentSound.DACType = SixteenBitDMA then
  1108.   Begin
  1109.     PlayingSixteenBit := True;
  1110.     InitSB16for16Out;
  1111.   End
  1112.   Else
  1113.   Begin
  1114.     PlayingSixteenBit := False;
  1115.     If SixteenBitCapable then
  1116.       InitSB16for8Out;
  1117.   End;
  1118.    (* Note that High Speed DMA xfers are not supported on the Sound Blaster *)
  1119.    (* 16 (since it doesn't need them), and should NOT be used on that class *)
  1120.    (* of sound card!!                                                       *)
  1121.   If not SixteenBitCapable then
  1122.   Begin
  1123.     If CurrentSound.Frequency >= 23000 then
  1124.       CurrentSound.DACType := HighSpeedDMA;
  1125.   End;
  1126.   ResetMixer;
  1127.   (* Then we have a stereo file, and for the SB-PRO, output in stereo... *)
  1128.   If TempHead.Phase = 1 then
  1129.     PlayStereo   (* Initialize the SB-PRO to play in stereo  *)
  1130.   Else PlayMono; (* We have a mono file, and reset the mixer *)
  1131.   CurrentSound.Phase := TempHead.Phase;
  1132.   FileXferLeft := FileSize (DSKFile) - SizeOf (TempHead);
  1133.   PlayFromDisk := True;
  1134.   If FileSize (DSKFile) > (MaxXferSize - SizeOf (TempHead)) then
  1135.   Begin
  1136.     CurrentSound.Buffer1Size := MaxXferSize div 2;
  1137.     CurrentSound.Buffer2Size := MaxXferSize div 2;
  1138.     BlockRead (DSKFile, CurrentSound.Buffer1^, (MaxXferSize - SizeOf (TempHead))
  1139.                          div 2);
  1140.     BlockRead (DSKFile, CurrentSound.Buffer2^, (MaxXferSize - SizeOf (TempHead))
  1141.                          div 2);
  1142.     If CurrentVolLevel <> NormalVol then
  1143.     Begin
  1144.       ChangeSampleVol (CurrentSound.Buffer1, MaxXferSize div 2);
  1145.       ChangeSampleVol (CurrentSound.Buffer2, MaxXferSize div 2);
  1146.     End;
  1147.     Dec (FileXferLeft, MaxXferSize);
  1148.     FinishUp := False;
  1149.   End
  1150.   Else
  1151.   Begin
  1152.     CurrentSound.Buffer1Size := FileSize (DSKFile) div 2;
  1153.     CurrentSound.Buffer2Size := FileSize (DSKFile) div 2;
  1154.     BlockRead (DSKFile, CurrentSound.Buffer1^, FileXferLeft div 2);
  1155.     BlockRead (DSKFile, CurrentSound.Buffer2^, FileXferLeft div 2);
  1156.     If CurrentVolLevel <> NormalVol then
  1157.     Begin
  1158.       ChangeSampleVol (CurrentSound.Buffer1, FileXferLeft div 2);
  1159.       ChangeSampleVol (CurrentSound.Buffer2, FileXferLeft div 2);
  1160.     End;
  1161.     Dec (FileXferLeft, FileSize (DSKFile));
  1162.     FinishUp := True;
  1163.   End;
  1164.   CurrentSound.BufferPlaying := 1;
  1165.   LeftToPlay := CurrentSound.Buffer1Size - 6;
  1166.   CurPos := GetAbsoluteAddress (CurrentSound.Buffer1) + 6;
  1167.   CurPageEnd := ((CurPos shr 16) shl 16) + 65536 - 1;
  1168.   Length := CurPageEnd - CurPos;
  1169.   VoiceEnd := CurPos + LeftToPlay;
  1170.   Playing := True;
  1171.   PlayFromDisk := True;
  1172.   SpeakerOn;
  1173.   PlayBack;
  1174. End;
  1175.  
  1176. Procedure PlaySound (Sound: BaseSoundType);
  1177. Begin
  1178.   LeftToPlay := Sound.BufferSize - 6;
  1179.   CurrentSound.DACType := Sound.DACType;
  1180.   CurrentSound.Frequency := Sound.Frequency;
  1181.   CurPos := GetAbsoluteAddress (Sound.Buffer) + 6;
  1182.   CurPageEnd := ((CurPos shr 16) shl 16) + 65536 - 1;
  1183.   Length := CurPageEnd - CurPos;
  1184.   VoiceEnd := CurPos + LeftToPlay;
  1185.   Playing := True;
  1186.   PlayFromDisk := False;
  1187.   If SixteenBitCapable then
  1188.   Begin
  1189.     If CurrentSound.DACType = SixteenBitDMA then
  1190.       InitSB16for16Out
  1191.     Else InitSB16for8Out;
  1192.   End;
  1193.   If CurrentVolLevel <> NormalVol then
  1194.     ChangeSampleVol (Sound.Buffer, Sound.BufferSize);
  1195.   SpeakerOn;
  1196.   PlayBack;
  1197. End;
  1198.  
  1199. Function ChangeBufferSize (Size: Word): Boolean;
  1200. Begin
  1201.   ChangeBufferSize := False;
  1202.   If ((not Playing) and (not Recording)) then
  1203.   Begin
  1204.     FreeMem (CurrentSound.Buffer1, MaxXferSize div 2);
  1205.     FreeMem (CurrentSound.Buffer2, MaxXferSize div 2);
  1206.     MaxXferSize := Size;
  1207.     ChangeBufferSize := True;
  1208.     GetMem (CurrentSound.Buffer1, MaxXferSize div 2);
  1209.     GetMem (CurrentSound.Buffer2, MaxXferSize div 2);
  1210.   End;
  1211. End;
  1212.  
  1213. Function CheckBufferSize: Word;
  1214. Begin
  1215.   CheckBufferSize := MaxXferSize;
  1216. End;
  1217.  
  1218. Procedure ClearInterrupt;
  1219. var
  1220.   Temp: Byte;
  1221. Begin
  1222.   If not PlayingSixteenBit then
  1223.     Temp := Port[DSP_DATA_AVAIL]
  1224.   Else
  1225.     Temp := Port[DSP_INT_ACK16];
  1226.   Port[PICPort - 1] := $20;
  1227. End;
  1228.  
  1229. Procedure DoFinishUp; near;
  1230. Begin
  1231.   If CurrentSound.BufferPlaying = 1 then
  1232.   Begin
  1233.     CurrentSound.BufferPlaying := 2;
  1234.     LeftToPlay := CurrentSound.Buffer2Size - 6;
  1235.     CurPos := GetAbsoluteAddress (CurrentSound.Buffer2) + 6;
  1236.     CurPageEnd := ((CurPos shr 16) shl 16) + 65536 - 1;
  1237.     Length := CurPageEnd - CurPos;
  1238.     VoiceEnd := CurPos + LeftToPlay;
  1239.     PlayBack;
  1240.   End
  1241.   Else
  1242.   Begin
  1243.     CurrentSound.BufferPlaying := 1;
  1244.     LeftToPlay := CurrentSound.Buffer1Size - 6;
  1245.     CurPos := GetAbsoluteAddress (CurrentSound.Buffer1) + 6;
  1246.     CurPageEnd := ((CurPos shr 16) shl 16) + 65536 - 1;
  1247.     Length := CurPageEnd - CurPos;
  1248.     VoiceEnd := CurPos + LeftToPlay;
  1249.     PlayBack;
  1250.   End;
  1251.   FinishUp := False;
  1252. End;
  1253.  
  1254. Procedure DoRecordToDisk; near;
  1255. Begin
  1256.   If CurrentSound.BufferPlaying = 1 then
  1257.   Begin
  1258.     CurrentSound.BufferPlaying := 2;
  1259.     LeftToPlay := CurrentSound.Buffer2Size - 6;
  1260.     CurPos := GetAbsoluteAddress (CurrentSound.Buffer2) + 6;
  1261.     CurPageEnd := ((CurPos shr 16) shl 16) + 65536 - 1;
  1262.     Length := CurPageEnd - CurPos;
  1263.     VoiceEnd := CurPos + LeftToPlay;
  1264.     PlayBack;
  1265.     BlockWrite (DSKFile, CurrentSound.Buffer1^, MaxXferSize div 2);
  1266.     LastSavedBuffer := 1;
  1267.     FillChar (CurrentSound.Buffer1^, MaxXferSize div 2, 0);
  1268.   End
  1269.   Else
  1270.   Begin
  1271.     CurrentSound.BufferPlaying := 1;
  1272.     LeftToPlay := CurrentSound.Buffer1Size - 6;
  1273.     CurPos := GetAbsoluteAddress (CurrentSound.Buffer1) + 6;
  1274.     CurPageEnd := ((CurPos shr 16) shl 16) + 65536 - 1;
  1275.     Length := CurPageEnd - CurPos;
  1276.     VoiceEnd := CurPos + LeftToPlay;
  1277.     PlayBack;
  1278.     BlockWrite (DSKFile, CurrentSound.Buffer2^, MaxXferSize div 2);
  1279.     LastSavedBuffer := 2;
  1280.     FillChar (CurrentSound.Buffer2^, MaxXferSize div 2, 0);
  1281.   End;
  1282. End;
  1283.  
  1284. Procedure DoPlayFromDisk; near;
  1285. Begin
  1286.     If FileXferLeft > 0 then
  1287.     Begin
  1288.       If FileXferLeft > (MaxXferSize div 2) then
  1289.       Begin
  1290.         If CurrentSound.BufferPlaying = 1 then
  1291.         Begin
  1292.           CurrentSound.BufferPlaying := 2;
  1293.           LeftToPlay := CurrentSound.Buffer2Size - 6;
  1294.           CurPos := GetAbsoluteAddress (CurrentSound.Buffer2) + 6;
  1295.           CurPageEnd := ((CurPos shr 16) shl 16) + 65536 - 1;
  1296.           Length := CurPageEnd - CurPos;
  1297.           VoiceEnd := CurPos + LeftToPlay;
  1298.           PlayBack;
  1299.           BlockRead (DSKFile, CurrentSound.Buffer1^, MaxXferSize div 2);
  1300.           CurrentSound.Buffer1Size := MaxXferSize div 2;
  1301.           If CurrentVolLevel <> NormalVol then
  1302.             ChangeSampleVol (CurrentSound.Buffer1, MaxXferSize div 2);
  1303.           Dec (FileXferLeft, MaxXferSize div 2);
  1304.         End
  1305.         Else
  1306.         Begin
  1307.           CurrentSound.BufferPlaying := 1;
  1308.           LeftToPlay := CurrentSound.Buffer1Size - 6;
  1309.           CurPos := GetAbsoluteAddress (CurrentSound.Buffer1) + 6;
  1310.           CurPageEnd := ((CurPos shr 16) shl 16) + 65536 - 1;
  1311.           Length := CurPageEnd - CurPos;
  1312.           VoiceEnd := CurPos + LeftToPlay;
  1313.           PlayBack;
  1314.           BlockRead (DSKFile, CurrentSound.Buffer2^, MaxXferSize div 2);
  1315.           If CurrentVolLevel <> NormalVol then
  1316.             ChangeSampleVol (CurrentSound.Buffer2, MaxXferSize div 2);
  1317.           CurrentSound.Buffer2Size := MaxXferSize div 2;
  1318.           Dec (FileXferLeft, MaxXferSize div 2);
  1319.         End;
  1320.       End
  1321.       Else
  1322.       Begin
  1323.         FinishUp := True;
  1324.         If CurrentSound.BufferPlaying = 1 then
  1325.         Begin
  1326.           CurrentSound.BufferPlaying := 2;
  1327.           LeftToPlay := CurrentSound.Buffer2Size - 6;
  1328.           CurPos := GetAbsoluteAddress (CurrentSound.Buffer2) + 6;
  1329.           CurPageEnd := ((CurPos shr 16) shl 16) + 65536 - 1;
  1330.           Length := CurPageEnd - CurPos;
  1331.           VoiceEnd := CurPos + LeftToPlay;
  1332.           PlayBack;
  1333.           BlockRead (DSKFile, CurrentSound.Buffer1^, FileXferLeft);
  1334.           CurrentSound.Buffer1Size := FileXferLeft;
  1335.           If CurrentVolLevel <> NormalVol then
  1336.             ChangeSampleVol (CurrentSound.Buffer1, FileXferLeft);
  1337.           FileXferLeft := 0;
  1338.         End
  1339.         Else
  1340.         Begin
  1341.           CurrentSound.BufferPlaying := 1;
  1342.           LeftToPlay := CurrentSound.Buffer1Size - 6;
  1343.           CurPos := GetAbsoluteAddress (CurrentSound.Buffer1) + 6;
  1344.           CurPageEnd := ((CurPos shr 16) shl 16) + 65536 - 1;
  1345.           Length := CurPageEnd - CurPos;
  1346.           VoiceEnd := CurPos + LeftToPlay;
  1347.           PlayBack;
  1348.           BlockRead (DSKFile, CurrentSound.Buffer2^, FileXferLeft);
  1349.           CurrentSound.Buffer2Size := FileXferLeft;
  1350.           If CurrentVolLevel <> NormalVol then
  1351.             ChangeSampleVol (CurrentSound.Buffer2, FileXferLeft);
  1352.           FileXferLeft := 0;
  1353.         End;
  1354.       End;
  1355.     End
  1356.     Else
  1357.     Begin
  1358.       (* Say that we have nothing more to say :-) *)
  1359.       Playing := False;
  1360.       PlayFromDisk := False;
  1361.       (* Turn off the speaker *)
  1362.       SpeakerOff;
  1363.       (* Close the file that we used *)
  1364.       Close (DSKFile);
  1365.     End;
  1366. End;
  1367.  
  1368. Procedure SBIntHandler; interrupt;
  1369. (***************************************************************************)
  1370. (* This procedure handles interrupt calls from the DMA chip when a xfer is *)
  1371. (* complete. As of version 1.01, this procedure has been expanded to read  *)
  1372. (* in a block off of a disk file as well. I have to try to make this code  *)
  1373. (* as fast as possible in the future, as it will "Interrupt" the main      *)
  1374. (* program, to do its own thing. And, in a game that can slow things down  *)
  1375. (* considerably. (Depending on sample rates, after all, sound that is      *)
  1376. (* recorded at 44Khz at 8 bits will xfer 44K to the DMA chip/second, that  *)
  1377. (* means with a 32K buffer, we won't even get 1 SECOND before the next     *)
  1378. (* interrupt!!!                                                            *)
  1379. (*                                                                         *)
  1380. (* Revisions:                                                              *)
  1381. (* ---------                                                               *)
  1382. (*   Version 1.01: Modified to handle disk reads into a "buffer", and then *)
  1383. (*                 output the buffer to the sound card. NO Double Buffering*)
  1384. (*   Version 2.0ß: Modified to handle Double Buffering AND Real-Time       *)
  1385. (*                 Voice Amplification. Also modified to handle DOS        *)
  1386. (*                 re-entrancy issues. Recording implemented as well.      *)
  1387. (*                 Made the function a LOT prettier with modules broken up!*)
  1388. (***************************************************************************)
  1389.  
  1390. var
  1391.   NewDTAArea: Array [1..128] of Byte;
  1392.   OldDTAArea: Pointer;
  1393. Begin
  1394.   asm CLI end;  (* NO Interrupts while in this code! *)
  1395.   (* First, we set the DOS critical error flag, so it uses that stack on   *)
  1396.   (* re-entry.                                                             *)
  1397.   If not Recording then
  1398.     InDOSFlag^ := 1;
  1399.   (* Now, we switch from the DOS PSP (if there is one, to ours)            *)
  1400.   Regs.AH := $51;
  1401.   MsDOS (Regs);
  1402.   TempPSP := Regs.BX;
  1403.   Regs.AH := $50;
  1404.   Regs.BX := TurboPSP;
  1405.   MsDOS (Regs);
  1406.   (* Get the Main Program's Data Transfer Area, and store it in OLDDTAArea *)
  1407.   Regs.AH := $2F;
  1408.   MsDOS (Regs);
  1409.   OldDTAArea := Ptr (Regs.ES, Regs.BX);
  1410.   (* Set the DTA to our temporary one, that we will use... *)
  1411.   Regs.AH := $1A;
  1412.   Regs.DS := Seg (NewDTAArea);
  1413.   Regs.DX := Ofs (NewDTAArea);
  1414.   MsDOS (Regs);
  1415.  
  1416.   If LeftToPlay > 0 then
  1417.     PlayBack
  1418.   Else if FinishUp then
  1419.     DoFinishUp
  1420.   Else if PlayFromDisk then
  1421.     DoPlayFromDisk
  1422.   Else if Recording then
  1423.     DoRecordToDisk
  1424.   Else
  1425.     Playing := False;
  1426.   (* Now, we switch to the DOS PSP (if there was one, from ours) *)
  1427.   Regs.AH := $50;
  1428.   Regs.BX := TempPSP;
  1429.   MsDOS (Regs);
  1430.   (* Now, we switch back to the OLD DTA... *)
  1431.   Regs.AH := $1A;
  1432.   Regs.DS := Seg (OldDTAArea);
  1433.   Regs.DX := Ofs (OldDTAArea);
  1434.   MsDOS (Regs);
  1435.   asm STI end;      (* Interupts are OK now! *)
  1436.   ClearInterrupt;   (* Tell the PIC chip that the interrupt is OVER *)
  1437. End;
  1438.  
  1439. Procedure StopSBIRQ;
  1440. Begin
  1441.   Port[PICPort] := Port[PICPort] OR IRQStopMask;
  1442. End;
  1443.  
  1444. Procedure StartSBIRQ;
  1445. Begin
  1446.   Port[PICPort] := Port[PICPort] AND IRQStartMask;
  1447. End;
  1448.  
  1449. Procedure SetMixerReg(Index, Value : Byte);
  1450. Begin
  1451.   Port[DSPPRO_INDEX] := Index;
  1452.   Port[DSPPRO_READWRITE] := Value;
  1453. End;
  1454.  
  1455. Function GetMixerReg(Index : Byte) : Byte;
  1456. Begin
  1457.   Port[DSPPRO_INDEX] := Index;
  1458.   GetMixerReg := Port[DSPPRO_READWRITE];
  1459. End;
  1460.  
  1461. Procedure ResetMixer;
  1462. Begin
  1463.   SetMixerReg (0, 0);
  1464. End;
  1465.  
  1466. Procedure PlayStereo;
  1467. Begin
  1468.   SetMixerReg ($E, $2);
  1469. End;
  1470.  
  1471. Procedure PlayMono;
  1472. Begin
  1473.   SetMixerReg ($E, $0);
  1474. End;
  1475.  
  1476. Procedure SetFMVolume (Left, Right: ProVolumeType);
  1477. Begin
  1478.   SetMixerReg ($26, Left SHL 4 + Right);
  1479. End;
  1480.  
  1481. Procedure SetCDVolume (Left, Right: ProVolumeType);
  1482. Begin
  1483.   SetMixerReg ($28, Left SHL 4 + Right);
  1484. End;
  1485.  
  1486. Procedure SetMasterVolume (Left, Right: ProVolumeType);
  1487. Begin
  1488.   SetMixerReg ($22, Left SHL 4 + Right);
  1489. End;
  1490.  
  1491. Procedure SetVocVolume (Left, Right: ProVolumeType);
  1492. Begin
  1493.   SetMixerReg ($4, Left SHL 4 + Right);
  1494. End;
  1495.  
  1496. Procedure SetMicVolume (Volume: MicVolumeType);
  1497. Begin
  1498.   SetMixerReg ($A, Volume);
  1499. End;
  1500.  
  1501. Procedure SetLineInVolume (Left, Right: ProVolumeType);
  1502. Begin
  1503.   SetMixerReg ($2E, Left SHL 4 + Right);
  1504. End;
  1505.  
  1506. Procedure SetInputSource (Filter: FilterType; TheInputs: InputSourceType);
  1507. Begin
  1508.   SetMixerReg ($C, Filter shl 3 + TheInputs shl 1);
  1509. End;
  1510.  
  1511. Procedure InstallHandler;
  1512. Begin
  1513.   StopSBIRQ;
  1514.   GetIntVec(IntHandler, @OldIntHandler);
  1515.   SetIntVec(IntHandler, @SBIntHandler);
  1516.   StartSBIRQ;
  1517.   IRQHandlerInstalled := True;
  1518. End;
  1519.  
  1520. Procedure UninstallHandler;
  1521. Begin
  1522.   StopSBIRQ;
  1523.   SetIntVec(IntHandler, @OldIntHandler);
  1524.   IRQHandlerInstalled := False;
  1525. End;
  1526.  
  1527. {$F+}
  1528. Procedure SBExitProc;
  1529. Begin
  1530.   ExitProc := OldExitProc;
  1531.   If Playing then
  1532.     DMAStop;
  1533.   If IRQHandlerInstalled then
  1534.     UninstallHandler;
  1535. End;
  1536. {$F-}
  1537.  
  1538. Procedure GetInDOSFlag;
  1539. Begin
  1540.   Regs.AH := $34;
  1541.   MsDOS (Regs);
  1542.   InDOSFlag := Ptr (Regs.ES, Regs.BX);
  1543.   Regs.AH := $51;
  1544.   MsDOS (Regs);
  1545.   TurboPSP := Regs.BX;
  1546. End;
  1547.  
  1548. Begin
  1549.   OldExitProc := ExitProc;
  1550.   ExitProc := @SBExitProc;
  1551.   Playing := False;
  1552.   Recording := False;
  1553.   GetMem (CurrentSound.Buffer1, MaxXferSize div 2);
  1554.   GetMem (CurrentSound.Buffer2, MaxXferSize div 2);
  1555.   GetInDOSFlag;
  1556.   LastSavedBuffer := 0;
  1557. End.